package csci6450.chickenFriedFS.nuggetServer.threads;

import csci6450.chickenFriedFS.nuggetCore.Nugget;
import csci6450.chickenFriedFS.nuggetCore.NuggetConstants;
import csci6450.chickenFriedFS.nuggetCore.NuggetServerInfo;
import csci6450.chickenFriedFS.nuggetServer.NuggetFileStoreManager;
import csci6450.chickenFriedFS.nuggetServer.NuggetServerLog;
import csci6450.chickenFriedFS.nuggetServer.RuntimeSettings;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Timer;

/**
 *  <p style="margin-top: 0">
 *        Extends Runnable. Listens for incoming connections from the 
 *        MetaServer(s) and Client(s)
 *      </p>
 */
// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
// #[regen=yes,id=DCE.EE67E59A-1F94-1D19-1ACD-C39763C149A7]
// </editor-fold> 
public class NuggetServerMetaListener extends Thread {

    /**
     * Socket for this server to run on.
     */
    private ServerSocket metaRequestServer;
    private MetaCheckInListener listener;

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.9CFD8DFE-DED4-FFA1-5A2A-91F1BC646D58]
    // </editor-fold> 
    public NuggetServerMetaListener() throws IOException {
        this.metaRequestServer = new ServerSocket(RuntimeSettings.getInstance().getMetaListenerPort());
        System.out.println("Server listening for Meta Server on port " + RuntimeSettings.getInstance().getMetaListenerPort() + ".");
        listener = new MetaCheckInListener();
        Timer timer = new Timer(30000, listener);
        listener.setTimer(timer);
        timer.start();
    }

    @Override
    public void run() {
        while (true) {
            try {
                Socket client = this.metaRequestServer.accept();
                System.out.println("Connected to Meta Server.");
                this.listener.reset();
                Connect c = new Connect(client);
            } catch (Exception e) {
                Logger.getLogger(NuggetServerMetaListener.class.getName()).log(Level.SEVERE, null, e);
            }
        }
    }

    class Connect extends Thread {

        private Socket client = null;
        private ObjectInputStream inputStream = null;
        private ObjectOutputStream outputStream = null;

        public Connect() {
        }

        public Connect(Socket clientSocket) {
            this.client = clientSocket;
            try {
                this.inputStream = new ObjectInputStream(this.client.getInputStream());
                this.outputStream = new ObjectOutputStream(this.client.getOutputStream());
            } catch (Exception e1) {
                try {
                    this.client.close();
                } catch (Exception e) {
                    Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, e);
                }
                return;
            }
            this.start();
        }

        @Override
        public void run() {
            try {

                Object received = this.inputStream.readObject();
                if (received instanceof Nugget) {
                    //this means we need to replicate this nugget and send it off somewhere
                    Nugget nug = (Nugget) received;
                    nug.setNugget(NuggetFileStoreManager.getInstance().getNugget(nug.getNuggetOffset()));
                    //close the client so the meta server can go do other shit.
                    client.close();
                    this.replicate(nug);
                } else {
                    Date timestamp = (Date) received;
                    //send the latest nuggets that have been added. Should work, but
                    //will need to be verified.
                    NuggetServerInfo info = new NuggetServerInfo(timestamp,
                            NuggetFileStoreManager.getInstance().getNumberOfAvalaibleNuggets(),
                            InetAddress.getLocalHost().getHostAddress(),
                            RuntimeSettings.getInstance().getClientListenerPort(),
                            RuntimeSettings.getInstance().getMetaListenerPort(),
                            NuggetServerLog.getInstance().getLatestNuggets(),
                            RuntimeSettings.getInstance().getNumberOfChunks(),
                            RuntimeSettings.getInstance().getReplicationPort());
                    this.outputStream.writeObject(info);
                    this.outputStream.flush();
                }
            } catch (Exception e) {
                Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, e);
            } finally {
                try {
                    this.client.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }

        private void replicate(Nugget nugget) throws IOException, ClassNotFoundException {
            Socket repSocket = null;
            try {
                repSocket = new Socket(RuntimeSettings.getInstance().getMetaServerIp(), RuntimeSettings.getInstance().getReplicationManagerPort());
                ObjectOutputStream out = new ObjectOutputStream(repSocket.getOutputStream());
                java.net.InetAddress i = java.net.InetAddress.getLocalHost();

                out.writeObject(i.getHostAddress());
                ObjectInputStream input = new ObjectInputStream(repSocket.getInputStream());
                NuggetServerInfo info = (NuggetServerInfo) input.readObject();
                repSocket.close();
                if (info != null) {
                    nugget.setNuggetRepLocation(info.getIpAddress());
                    nugget.setNuggetRepPort(info.getClientPort());

                    System.out.println("Force replicatate to "+info.getIpAddress()+":"+info.getReplicationPort()+"");
                    repSocket = new Socket(info.getIpAddress(), info.getReplicationPort());
                    out = new ObjectOutputStream(repSocket.getOutputStream());
                    input = new ObjectInputStream(repSocket.getInputStream());
                    Integer repPort = (Integer) input.readObject();
                    repSocket.close();

                    repSocket = new Socket(info.getIpAddress(), repPort);
                    out = new ObjectOutputStream(repSocket.getOutputStream());
                    out.writeObject(nugget);
                    input = new ObjectInputStream(repSocket.getInputStream());
                    String response = (String) input.readObject();

                    if (response.equals(NuggetConstants.SUCCESS)) {
                        NuggetServerLog.getInstance().addNugget(nugget);
                    }
                }
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
                throw ex;
            } catch (UnknownHostException ex) {
                ex.printStackTrace();
                throw ex;
            } catch (IOException ex) {
                ex.printStackTrace();
                throw ex;
            } finally {
                if (repSocket != null) {
                    repSocket.close();
                }

            }
        }
    }
}

